package org.dru.clay.util.functional;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * CollectionUtils
 * User: joakimd
 * Date: 2013-07-22
 * Time: 15:45
 */
public final class CollectionUtils {
    private CollectionUtils() {
    }

    public static <T> Collection<T> build(final T source, final Predicate<T> predicate, final Function<T, T> function) {
        final Collection<T> result = new ArrayList<T>();
        T current = source;
        while (predicate.apply(current)) {
            current = function.apply(current);
            result.add(current);
        }
        return result;
    }

    public static <T> void forEach(final Iterable<T> source, final Block<T> block) {
        for (final T value : source) {
            block.apply(value);
        }
    }

    public static <T> boolean every(final Iterable<T> source, final Predicate<T> predicate) {
        for (final T value : source) {
            if (!predicate.apply(value)) {
                return false;
            }
        }
        return true;
    }

    public static <T> boolean some(final Iterable<T> source, final Predicate<T> predicate) {
        for (final T value : source) {
            if (predicate.apply(value)) {
                return true;
            }
        }
        return false;
    }

    public static <T> Collection<T> filter(final Iterable<T> source, final Predicate<T> predicate) {
        final Collection<T> result = new ArrayList<T>();
        for (final T value : source) {
            if (predicate.apply(value)) {
                result.add(value);
            }
        }
        return result;
    }

    public static <F, T> Collection<T> transform(final Iterable<F> source, final Function<F, T> function) {
        final Collection<T> result = new ArrayList<T>();
        for (final F value : source) {
            result.add(function.apply(value));
        }
        return result;
    }

    public static <F, T> T fold(final Iterable<F> source, final T initializer, final Reducer<F, T> reducer) {
        T result = initializer;
        for (final F value : source) {
            result = reducer.apply(result, value);
        }
        return result;
    }

    public static <T> T fold(final Iterable<T> source, final Reducer<T, T> reducer) {
        final Iterator<T> it = source.iterator();
        T result = null;
        if (it.hasNext()) {
            result = it.next();
            while (it.hasNext()) {
                result = reducer.apply(result, it.next());
            }
        }
        return result;
    }
}
